/*
 * $Id: TcpGate.cc 396 2010-10-01 13:09:39Z chunghh $
 */

#include "TcpGate.h"
#include "GateProc.h"
#include "Globals.h"
#include "Command.h"
#include <xp/bas/MutexLock.hpp>

#ifdef __linux
        #include <linux/unistd.h>
	#include <sys/syscall.h>
#endif

TcpGate::TcpGate (const xp_char_t* name): Gate (name)
{
}

int TcpGate::start (int flags)
{
	stop_requested = false;
	return Thread::start (flags);
}

int TcpGate::main ()
{
	setMaxConnections (app_main->env.data.gateMaxConnections());

	XP_LOG1 (&app_log, name(), XP_LOG_INFO, 
		XP_T("configured to listen on %s"), 
		(const xp_char_t*)binding_address.numericService());

	// set the stack size to 2MB
	TcpServer::setThreadStackSize (1024u * 1024u * 2);

	int err_code, fail_count = 0;
restart:
	if (!stop_requested && TcpServer::start(&err_code) == -1) 
	{
		XP_LOG0 (&app_log, name(), XP_LOG_CRITICAL,
			XP_T("failed to start the service"));
		if (fail_count < 5)
		{
			Thread::sleep (1000);
			fail_count++;
			XP_LOG1 (&app_log, name(), XP_LOG_CRITICAL,
				XP_T("retrying to start the service #%d"),
				fail_count
			);
			goto restart;
		}
		return -1;
	}

	return 0;
}

int TcpGate::stop ()
{
	stop_requested = true;
	return TcpServer::stop ();
}

int TcpGate::handle_client (TcpSocket* sock, TcpAddress* addr)
{
	xp_char_t new_name[MAX_NAME_LEN+1];
#ifdef SYS_gettid
	xp_sprintf (new_name, xp_countof(new_name), 
		XP_T("%s/%d"), name(), (int)syscall(SYS_gettid));
#else
	xp_sprintf (new_name, xp_countof(new_name), 
		XP_T("%s/%x"), name(), (unsigned int)pthread_self());
#endif
	GateProc proc (new_name, sock, addr, this);

	XP_LOG0 (&app_log, new_name, XP_LOG_DEBUG,
		XP_T("client connection established"));

	SttpCmd cmd;
	while (!isStopRequested() && !proc.isExitRequested()) 
	{
		if (proc.receiveCmd(&cmd) == -1) 
		{
			if (proc.errorCode() == Sttp::ERR_RECEIVE) break;

			xp_char_t buf[100];
			xp_sprintf (buf, xp_countof(buf), XP_T("%d"), proc.errorCode());
			const xp_char_t* err = proc.errorStr();
			proc.sendCmd (CMD_ERR, 2, buf, err);
			XP_LOG1 (&app_log, new_name, XP_LOG_INFO,
				XP_T("cmd reception error - %s"), err);
			break;
		}

		/* TODO: encryption
		{
			MutexLock ms (&gRunEnvMutex);
			AppEnv::EncMode encMode = app_main->env.data.encMode();
			if (encMode == AppEnv::ENC_ENABLED) proc.setOptEncSend (true);
			else if (encMode == AppEnv::END_DISABLED) proc.setOptEncSend (false);
			else proc.setOptEncSend (proc.optEncReceive());
		}
		*/

		if (proc.processCmd(cmd) == -1) 
		{
			xp_char_t buf[100];
			xp_sprintf (buf, xp_countof(buf), XP_T("%d"), proc.errorCode());
			
			const xp_char_t* err = proc.errorStr();
			proc.sendCmd (CMD_ERR, 2, buf, err);
			XP_LOG2 (&app_log, new_name, XP_LOG_INFO,
				XP_T("cmd processing error - %s [%s]"), 
				err, (const xp_char_t*)cmd.name);
			break;
		}
	}

	XP_LOG0 (&app_log, new_name, XP_LOG_DEBUG,
		XP_T("client connection closed"));

	return 0;
}
